home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-08-02 | 12.2 KB | 560 lines | [TEXT/KAHL] |
- //
- // VV.c
- //
- // Written by Alastair Rankine
- // Copyright © 1993 All Rights Reserved.
- // And All That Sort of Stuff.
- //
- // Version History:
- // 1.0 - First Development
- //
- // 1.1 - Upgraded to DragonSmith 1.1
- // - Made ProcessDoc _far_ more intelligent. Is alias aware, can recognise
- // existing version numbers (even hidden amongst whitespace), and is more
- // bulletproof in general.
- // - Added preferences. Can now specify whether or not to quit after
- // processing, whether or not to ignore aliases, and whether or not
- // to try to replace existing version numbers.
- // - Status dialog box. Non-modal and quite hoopy all round really.
- //
-
- #include <string.h>
- #include <ctype.h>
-
- #include "Dragon.h"
- #include "EventUtils.h"
-
- // System 7.0 only Dialog Manager routines. These are out of technote something or other,
- // and not included in the standard THINK C header files.
- pascal OSErr GetStdFilterProc(ModalFilterProcPtr *theProc)
- = { 0x303C, 0x0203, 0xAA68 }; // Returns a pointer to the Dialog Manager's standard dialog filter
- pascal OSErr SetDialogDefaultItem(DialogPtr theDialog, short newItem)
- = { 0x303C, 0x0304, 0xAA68 }; // Indicates to the dialog manager which item is default. Will then alias the return
- // & enter keys to this item, and also bold border it for you (yaaaaa!)
- pascal OSErr SetDialogCancelItem(DialogPtr theDialog, short newItem)
- = { 0x303C, 0x0305, 0xAA68 }; // Indicates which item should be aliased to escape or Command - .
- pascal OSErr SetDialogTracksCursor(DialogPtr theDialog, Boolean tracks)
- = { 0x303C, 0x0306, 0xAA68 }; // Tells the dialog manager that there is an edit line in this dialog, and
- // it should track and change to an I-Beam cursor when over the edit line
-
- // Pascal string compare function used by ProcessFile():
- int Pstrcmp(const unsigned char *s1, const unsigned char *s2);
-
- // Used in the Preferences Dialog:
- pascal void lineProc(WindowPtr w, short item);
-
- // Is this string a version number?
- char IsVersionStr(char *s);
-
- // Item numbers for the preferences dialog:
- enum {
- iOK = 1,
- iCancel,
- iQuitAfter,
- iReplaceExistVNum,
- iIgnoreAlias,
- iTitle,
- iLine
- };
-
- // Item numbers for status dialog:
- enum {
- iStop = 1,
- iProcTxt,
- iCurFile
- };
-
- // Our preferences:
- enum {
- prefVersPrefs = prefDragonPrefs + 1
- };
-
- // Menu item number for the Preferences… dialog:
- const int itemPrefs = 8;
-
- // Our dialog IDs:
- enum {
- dlogPref = 128,
- dlogStatus
- };
-
- // Our preferences:
- struct VPrefs {
- char quitAfter;
- char replaceExistVNum;
- char ignoreAliases;
- };
-
- //
- // The class definition itself!
- //
- class DVers: public Dragon {
-
- protected:
- struct VPrefs **VersPrefs; // The prefs stored in the prefs file
- struct VPrefs p; // Prefs to use at run-time.
-
- DialogPtr stat; // The status dialog, if any.
- Handle curFile; // Handle to the static text item for the
- // current file name.
-
- public:
- DVers(void);
- void ProcessFile(void);
-
- protected:
- void DoAbout(void);
- void AdjustMenusBusy(void);
- void AdjustMenusIdle(void);
- void DoMenu(long menuItemCode);
- void DoMouseDown(EventRecord *theEvent);
- void DoDrag(WindowPtr wind, Point p);
- void DoPrefsDialog(void);
- void ReadPrefs(void);
- void BeginProcessing(void);
- void DoEvent(EventRecord *er);
- void EndProcessing(void);
- };
-
- Dragon *CreateGDragon (void)
- {
- return (Dragon *) new DVers;
- }
-
- DVers::DVers (void)
- {
- VersPrefs = NULL;
- p.quitAfter = 0;
- p.replaceExistVNum = 1;
- p.ignoreAliases = 0;
- dirDepthLimit = -10;
- autoQuit = FALSE;
- }
-
- void
- DVers::DoAbout(void)
- {
- Alert(128, nil);
- }
-
- void
- DVers::ProcessFile (void)
- {
- short rf = 0, i;
- Handle vers;
- FSSpec copy;
- OSErr err;
-
- // Display the filename:
- if(curFile)
- SetIText(curFile, curDocFSS->name);
-
- if(!p.ignoreAliases) {
- Boolean fold, alias;
-
- // Make a copy of the FSSpec, ready to hold the FSSpec of the resolved alias file.
- BlockMove(curDocFSS, ©, sizeof(FSSpec));
- err = ResolveAliasFile(©, 1, &fold, &alias);
-
- // If it's a folder, or the alias can't be resolved, exit quietly...
- if(fold || alias && (err == nsvErr || err == fnfErr || err == dirNFErr))
- goto yo_later_dude;
- else if(err) {
- Error(err);
- goto yo_later_dude;
- }
-
- rf = FSpOpenResFile(©, fsRdPerm);
- } else
- rf = FSpOpenResFile(curDocFSS, fsRdPerm);
-
- if(rf == -1) {
- err = ResError();
-
- // File may either have no resource fork or an empty one, in which case we
- // exit quietly:
- if(err && err != resFNotFound && err != eofErr)
- Error(err);
-
- rf = 0;
- goto yo_later_dude;
- }
-
- if(vers = Get1Resource('vers', 1)) {
- short namelen = curDocFSS->name[0], verslen = (*vers)[6];
- Str255 n, v; // New name and version number, respectively.
- char *lastWd, *versNo = (char *)v;
-
- // Copy the version string
- BlockMove(*vers + 7, v, verslen);
- v[verslen] = 0;
-
- // Don't need the resource file any more...
- CloseResFile(rf);
- err = ResError();
- if(err) {
- Error(err);
- goto yo_later_dude;
- }
-
- // Copy the old name into n + 1 (leaving room for the length byte)
- BlockMove(curDocFSS->name + 1, n + 1, namelen);
- n[namelen + 1] = 0;
-
- // Remove trailing space on the name:
- lastWd = (char *)n + namelen;
- while(isspace(*lastWd) && (lastWd > (char *)n))
- *lastWd-- = 0;
-
- // First check the version string: some applications (eg Speedometer)
- // store the application name in here as well, believe it or not. What
- // we do to get around this is to iterate through the string, word
- // by word until a valid version string is found. If one can't be found,
- // we abort.
- do {
- // Have we found a version string
- if(IsVersionStr(versNo))
- break;
-
- // Go to the next word:
- if(versNo = strchr(versNo, ' '))
- versNo++;
-
- } while(versNo);
-
- if(!versNo)
- goto yo_later_dude;
- else {
- // Null-terminate the version string:
- char *c = versNo;
-
- do
- c++;
- while(*c && !isspace(*c));
-
- *c = 0;
- }
-
- // Get the last word of the filename:
- lastWd = strrchr((char *)n + 1, ' ');
-
- // If there's more than one word, we need to investigate further...
- if(lastWd) {
- // Reset lastWd if there was no version number:
- if(!IsVersionStr(lastWd + 1))
- lastWd = NULL;
-
- // Or abort if we're not to replace the one that's there:
- else if(!p.replaceExistVNum)
- goto yo_later_dude;
- }
-
- // No version number? Well set lastWd to point to where one should be!
- if(!lastWd)
- lastWd = (char *)n + namelen;
-
- // Remove any existing trailing spaces:
- while(isspace(*lastWd) && (lastWd > (char *)n))
- lastWd--;
-
- // Add a space before the new version string:
- *++lastWd = ' ';
-
- // Append the version string.
- strcpy(lastWd + 1, versNo);
-
- // Convert to a pascal string and check the length.
- n[0] = strlen((char *)n + 1);
- if(n[0] >= 63)
- goto yo_later_dude;
-
- // Do the rename if the filename has changed:
- if(Pstrcmp(curDocFSS->name, n)) {
- err = FSpRename(curDocFSS, n);
- if(err) {
- Error(err);
- goto yo_later_dude;
- }
-
- // Now tell the finder about it:
- BlockMove(curDocFSS, ©, sizeof(FSSpec));
- CopyPStr(n, copy.name);
- FSpRefreshFinderDisplay(©);
- }
-
- } else {
- CloseResFile(rf);
- err = ResError();
- if(err)
- Error(err);
- }
-
- yo_later_dude:
- if(rf)
- CloseResFile(rf);
- }
-
- void
- DVers::BeginProcessing(void)
- {
- Rect r;
-
- inherited::BeginProcessing();
-
- stat = GetNewDialog(dlogStatus, nil, (WindowPtr)-1L);
- if(!stat)
- return;
-
- // Set the GrafPort
- SetPort(stat);
-
- // Set the default buttons
- SetDialogCancelItem(stat, 1);
-
- // Set the curFile variable;
- GetDItem(stat, iCurFile, &r, &curFile, &r);
- }
-
- void
- DVers::DoEvent(EventRecord *er)
- //
- // Handle our modeless status dialog if neccessary.
- //
- {
- if(IsDialogEvent(er)) {
- int item = 0;
- DialogPtr dlog = FrontWindow();
-
- if(er->what == keyDown) {
-
- // Check for Esc or cmd-. keypresses.
- if(IsCancelEvent(er))
- item = iStop;
-
- // Get any? (ooer!)
- if(item) {
- ControlHandle ch;
- Rect r;
-
- // Toggle the control on and off again to give some feedback:
- GetDItem(dlog, item, &r, &ch, &r);
- HiliteControl(ch, 1);
- Delay(4, &r);
- HiliteControl(ch, 0);
- }
- }
-
- // Let the dialog manager do whatever's neccessary:
- DialogSelect(er, &dlog, &item);
-
- // Have we aborted?
- if(dlog == stat && item == iStop)
- StopProcessing(noErr);
-
- } else
- inherited::DoEvent(er);
- }
-
- void
- DVers::DoMouseDown(EventRecord *theEvent)
- {
- WindowPtr whichWindow;
- short domain;
-
- domain = FindWindow(theEvent->where, &whichWindow);
- switch (domain) {
- case inDrag:
- DoDrag(whichWindow, theEvent->where);
- break;
- default:
- inherited::DoMouseDown(theEvent);
- break;
- }
- }
-
- void
- DVers::DoDrag(WindowPtr wind, Point mPoint)
- {
- if(wind != FrontWindow())
- SelectWindow(wind);
- else
- DragWindow(wind, mPoint, &screenBits.bounds);
- }
-
- void
- DVers::EndProcessing(void)
- {
- inherited::EndProcessing();
-
- // Set this variable if our prefs tell us to quit!
- autoQuit = p.quitAfter;
-
- // Get rid of the status box if neccessary:
- if(stat) {
- DisposDialog(stat);
- stat = NULL;
- curFile = NULL;
- }
- }
-
- void
- DVers::DoMenu (long menuItemCode)
- {
- short menuID, itemNum;
-
- menuID = menuItemCode >> 16;
- itemNum = menuItemCode & 0xFFFF;
-
- // Our preferences… menu item needs to be checked for...
- if (menuID == mEdit && itemNum == itemPrefs)
- DoPrefsDialog();
- else
- inherited::DoMenu (menuItemCode);
- }
-
- void
- DVers::AdjustMenusBusy (void)
- {
- inherited::AdjustMenusBusy ();
- DisableItem (editMenu, itemPrefs); // Disable the Preferences… item
- }
-
- void
- DVers::AdjustMenusIdle (void)
- {
- inherited::AdjustMenusIdle ();
- EnableItem (editMenu, itemPrefs);
- }
-
- void
- DVers::DoPrefsDialog(void)
- //
- // Puts a modal (urk! I know!) dialog box up to edit the preferences.
- //
- {
- DialogPtr dlog;
- Rect r;
- ControlHandle CtlQuitAfter, CtlIgnoreAlias, CtlReplaceExistVNum;
- int item;
-
- dlog = GetNewDialog(dlogPref, nil, (WindowPtr)-1L);
- if(!dlog)
- return;
- SetPort(dlog);
-
- // Set the default buttons
- SetDialogDefaultItem(dlog, iOK);
- SetDialogCancelItem(dlog, iCancel);
-
- // Set the lineProc item:
- GetDItem(dlog, iLine, &item, &CtlQuitAfter, &r);
- SetDItem(dlog, iLine, item, lineProc, &r);
-
- // Get the control handles:
- GetDItem(dlog, iQuitAfter, &item, &CtlQuitAfter, &r);
- GetDItem(dlog, iIgnoreAlias, &item, &CtlIgnoreAlias, &r);
- GetDItem(dlog, iReplaceExistVNum, &item, &CtlReplaceExistVNum, &r);
-
- SetCtlValue(CtlQuitAfter, p.quitAfter);
- SetCtlValue(CtlIgnoreAlias, p.ignoreAliases);
- SetCtlValue(CtlReplaceExistVNum, p.replaceExistVNum);
-
- do {
- ControlHandle ch = NULL;
-
- ModalDialog(nil, &item);
- switch (item) {
- case iQuitAfter:
- ch = CtlQuitAfter;
- break;
- case iIgnoreAlias:
- ch = CtlIgnoreAlias;
- break;
- case iReplaceExistVNum:
- ch = CtlReplaceExistVNum;
- break;
- default:
- break;
- }
-
- // Toggle the control:
- if(ch)
- SetCtlValue(ch, !GetCtlValue(ch));
-
- } while (item != iOK && item != iCancel);
-
- if (item == OK) {
- p.quitAfter = GetCtlValue(CtlQuitAfter);
- p.ignoreAliases = GetCtlValue(CtlIgnoreAlias);
- p.replaceExistVNum = GetCtlValue(CtlReplaceExistVNum);
-
- // Save the preferences:
- BlockMove(&p, *VersPrefs, sizeof(struct VPrefs));
- preferences->SavePrefResource(prefVersPrefs);
- }
-
- DisposDialog(dlog);
- }
-
- void DVers::ReadPrefs (void)
- {
- inherited::ReadPrefs ();
-
- VersPrefs = (struct VPrefs **) preferences->GetPrefResource (prefVersPrefs);
- if (VersPrefs)
- BlockMove(*VersPrefs, &p, sizeof(struct VPrefs));
- }
-
- pascal void
- lineProc(WindowPtr w, short item)
- //
- // Does a double line in the relevant user item...
- //
- {
- Handle h;
- Rect r;
-
- GetDItem(w, item, &h, &h, &r);
- MoveTo(r.left, r.top);
- LineTo(r.right, r.top);
- MoveTo(r.left, r.bottom - 1);
- LineTo(r.right, r.bottom - 1);
- }
-
- int
- Pstrcmp(const unsigned char *s1, const unsigned char *s2)
- //
- // like strcmp, only for pascal strings (!)
- //
- {
- int i = *s1;
-
- if (i == *s2)
- for (; i; i--)
- if (s1[i] != s2[i])
- return(i);
- return(i);
- }
-
- char
- IsVersionStr(char *c)
- //
- // Returns true if the word (ie delimited by null or whitespace) pointed to by wd
- // is a valid version number.
- //
- {
- char alphCount = 2;
-
- for(; *c && !isspace(*c) && alphCount; c++) {
- // Not a version number if it has more than one alphabetic character...
- if(isalpha(*c))
- alphCount--;
-
- // Version numbers can only have digits or dots...
- else if(!isdigit(*c) && *c != '.')
- alphCount = 0;
- }
-
- return(alphCount != 0);
- }
-